import {
  type ChangeEvent,
  forwardRef,
  type MouseEventHandler,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';
import classNames from 'classnames';
import type {
  IClientFile,
  IUploadMessageSendFile,
  IUploadMessageSendFileBody,
  IUploadPostContent,
  IUploadPostContentBody,
  IUploadPostNewFile,
  IUploadPostNewFileBody,
  IUploadPostTemplateFile,
  IUploadPostTemplateFileBody,
  IUploadSectionContentBody,
  IUploadSectionContentFile,
} from '@/interfaces';
import type { TBody, TError, TMessageType, TToast } from '@/types';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
  queryUserFiles,
  removeFile,
  uploadMessageSendFile,
  uploadPostContent,
  uploadPostNewFile,
  uploadPostTemplateFile,
  uploadSectionContent,
} from '@/services/api';
import Image from 'next/image';
import { useTranslations } from 'use-intl';

export default forwardRef(function YwBox(
  {
    id,
    oss,
    type,
  }: {
    id?: string | number;
    oss?: string;
    type: 'message' | 'newPost' | 'editPost' | 'postTemplate' | 'section';
  },
  ref,
) {
  const t = useTranslations('PostEditPage');
  const modalRef = useRef(null);
  const uploadFilePanelRef = useRef(null);
  const browseFilePanelRef = useRef(null);
  const [tabIndex, setTabIndex] = useState(99);
  const [browseFileItemIndex, setBrowseFileItemIndex] = useState(-1);
  const [form, setForm] = useState({
    uploadFile: '',
    file: '',
    fileUrl: '',
  });
  const [alert, setAlert] = useState('Click to upload file');
  const [alertType, setAlertType] = useState<TMessageType>('LIGHT');
  const [browseFileAlert, setBrowseFileAlert] = useState(
    'Click to upload file',
  );
  const [browseFileAlertType, setBrowseFileAlertType] =
    useState<TMessageType>('LIGHT');
  const [options, setOptions] = useState<{
    onClose: Function;
    onSelect: Function;
  }>();

  const clientQueryUserFilesQuery = useQuery(
    ['/file', '/user'],
    async () => {
      return (await queryUserFiles()) as IClientFile[];
    },
    {
      enabled: tabIndex === 2,
    },
  );

  const uploadSectionContentMutation = useMutation(
    async (variables: TBody<IUploadSectionContentBody>) => {
      return (await uploadSectionContent(
        variables,
      )) as IUploadSectionContentFile;
    },
  );
  const uploadPostContentMutation = useMutation(
    async (variables: TBody<IUploadPostContentBody>) => {
      return (await uploadPostContent(variables)) as IUploadPostContent;
    },
  );
  const uploadPostNewContentMutation = useMutation(
    async (variables: TBody<IUploadPostNewFileBody>) => {
      return (await uploadPostNewFile(variables)) as IUploadPostNewFile;
    },
  );
  const uploadPostTemplateFileMutation = useMutation(
    async (variables: TBody<IUploadPostTemplateFileBody>) => {
      return (await uploadPostTemplateFile(
        variables,
      )) as IUploadPostTemplateFile;
    },
  );
  const uploadMessageSendFileMutation = useMutation(
    async (variables: TBody<IUploadMessageSendFileBody>) => {
      return (await uploadMessageSendFile(variables)) as IUploadMessageSendFile;
    },
  );
  const clientRemoveFileMutation = useMutation(
    async (variables: TBody<void>) => {
      await removeFile(variables);
    },
  );

  useImperativeHandle(ref, () => ({
    open,
  }));

  function show(toast: TToast, type = 1) {
    let message = toast.message;
    if (type === 1) {
      setAlert(message);
      setAlertType(toast.type!);
    } else if (type === 2) {
      setBrowseFileAlert(message);
      setBrowseFileAlertType(toast.type!);
    } else {
      throw 'Unknown error';
    }
  }

  function open(options: { onClose: Function; onSelect: Function }) {
    if (!modalRef.current) {
      return;
    }
    // @ts-ignore
    const modal = bootstrap.Modal.getOrCreateInstance(modalRef.current);
    modal.show();
    setOptions(options);
  }

  function hide() {
    if (!modalRef.current) {
      return;
    }
    // @ts-ignore
    const modal = bootstrap.Modal.getOrCreateInstance(modalRef.current);
    modal.hide();

    if (!options) {
      return;
    }
    options.onClose();
  }

  function onClickUploadFileNavBtn() {
    setTabIndex(1);
    const current = browseFilePanelRef.current;
    if (current) {
      // @ts-ignore
      const collapse = bootstrap.Collapse.getOrCreateInstance(current);
      collapse.hide();
    }
  }

  function onClickBrowseFileNavBtn() {
    setTabIndex(2);
    const current = uploadFilePanelRef.current;
    if (current) {
      // @ts-ignore
      const collapse = bootstrap.Collapse.getOrCreateInstance(current);
      collapse.hide();
    }
  }

  function onClickBrowseFileItem(item: IClientFile) {
    setBrowseFileItemIndex(item.id);
    show(
      {
        type: 'LIGHT',
        message: t('clickSelectButtonToInsertFile'),
      },
      2,
    );
  }

  async function onClickDeleteFileBtn() {
    try {
      if (browseFileItemIndex === -1) {
        show(
          {
            type: 'DANGER',
            message: t('selectFileToDelete'),
          },
          2,
        );
        return;
      }

      await clientRemoveFileMutation.mutateAsync({
        id: browseFileItemIndex,
      });

      setBrowseFileItemIndex(-1);
      await clientQueryUserFilesQuery.refetch();

      show(
        {
          type: 'SUCCESS',
          message: t('deleteCompleted'),
        },
        2,
      );
    } catch (e: any) {
      clientRemoveFileMutation.reset();
      show(
        {
          type: 'DANGER',
          message: e,
        },
        2,
      );
    }
  }

  function onClickSelectFileBtn() {
    if (tabIndex === 1) {
      show({
        type: 'INFO',
        message: t('uploadFileOrClickBrowseButton'),
      });
      return;
    } else if (tabIndex === 2) {
      const files = clientQueryUserFilesQuery.data as IClientFile[];
      if (!files.length) {
        show(
          {
            type: 'INFO',
            message: t('fileNotFoundUploadToProceed'),
          },
          2,
        );
        return;
      } else if (browseFileItemIndex === -1) {
        show(
          {
            type: 'INFO',
            message: 'Please click Choose File',
          },
          2,
        );
        return;
      }
    }

    //
    if (!options) {
      show(
        {
          type: 'DANGER',
          message: t('operationFailed'),
        },
        2,
      );
      return;
    }

    if (tabIndex === 1) {
      show({
        type: 'WARNING',
        message: '未选择文件',
      });
      return;
    }

    const data = clientQueryUserFilesQuery.data as IClientFile[];
    const find = data.find((item) => item.id === browseFileItemIndex);
    if (!find) {
      show(
        {
          type: 'DANGER',
          message: t('selectedFileNotFound'),
        },
        2,
      );
      return;
    }
    options.onSelect({
      ...find,
      urls: { ...find.urls, default: oss + find.urls.default },
    });
    hide();
  }

  function checkForm(name: string) {
    if (name === 'uploadFile') {
      const { uploadFile, file } = form;
      if (!uploadFile || !file) {
        const elementById = document.getElementById('ywbox-input-uploadFile');
        if (elementById) {
          elementById.focus();
        }
        throw t('fileRequired');
      }
    }
  }

  async function onClickUploadFileBtn() {
    try {
      checkForm('uploadFile');
      const { file } = form;
      const body = {
        file,
      };

      let result;
      if (type === 'editPost') {
        result = await uploadPostContentMutation.mutateAsync({
          id: id as number,
          data: body,
        });
      } else if (type === 'newPost') {
        result = await uploadPostNewContentMutation.mutateAsync({
          data: body,
        });
      } else if (type === 'section') {
        result = await uploadSectionContentMutation.mutateAsync({
          id: id as number,
          data: body,
        });
      } else if (type === 'message') {
        result = await uploadMessageSendFileMutation.mutateAsync({
          id: id as number,
          data: body,
        });
      } else if (type === 'postTemplate') {
        result = await uploadPostTemplateFileMutation.mutateAsync({
          id: id as number,
          data: body,
        });
      } else {
        show({
          type: 'DANGER',
          message: t('uploadTypeNotFound'),
        });
        return;
      }

      setForm({
        ...form,
        uploadFile: '',
        file: '',
        fileUrl: result.urls.default,
      });

      show({
        type: 'SUCCESS',
        message: t('uploadCompleted'),
      });
    } catch (e) {
      if (type === 'section') {
        uploadSectionContentMutation.reset();
      } else if (type === 'message') {
        uploadMessageSendFileMutation.reset();
      } else if (type === 'postTemplate') {
        uploadPostTemplateFileMutation.reset();
      } else if (type === 'editPost') {
        uploadPostContentMutation.reset();
      } else if (type === 'newPost') {
        uploadPostNewContentMutation.reset();
      }

      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  function onClickCloseModalBtn() {
    hide();
  }

  function onChangeForm(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;

    if (name === 'uploadFile') {
      const file = (e.target as any).files[0];
      setForm({
        ...form,
        [name]: value,
        file,
      });
      show({
        type: 'SECONDARY',
        message: t('clickBrowseButtonToViewFile'),
      });
      return;
    }
  }

  return (
    <div
      ref={modalRef}
      className="modal modal-xl fade"
      data-bs-backdrop="static"
      data-bs-keyboard="false"
      tabIndex={tabIndex}
      aria-labelledby="staticBackdropLabel"
      aria-hidden="true"
    >
      <div className="modal-dialog modal-dialog-scrollable">
        <div className="modal-content">
          <div className="modal-header">
            <h5 className="modal-title" id="staticBackdropLabel">
              {t('fileManager')}
            </h5>
            <button
              onClick={onClickCloseModalBtn}
              type="button"
              className="btn-close"
              aria-label="Close"
            ></button>
          </div>
          <div className="modal-body">
            {/* nav */}
            <ul className="nav justify-content-center user-select-none">
              <li className="nav-item">
                <a
                  onClick={onClickUploadFileNavBtn}
                  className={classNames('nav-link', {
                    active: tabIndex === 1,
                    'link-dark': tabIndex !== 1,
                  })}
                  aria-current="page"
                  data-bs-toggle="collapse"
                  href="#ywbox-nav-upload-file"
                  role="button"
                  aria-expanded="true"
                  aria-controls="ywbox-nav-upload-file"
                >
                  {t('uploadFile')}
                </a>
              </li>
              <li className="nav-item">
                <a
                  onClick={onClickBrowseFileNavBtn}
                  className={classNames('nav-link', {
                    active: tabIndex === 2,
                    'link-dark': tabIndex !== 2,
                  })}
                  data-bs-toggle="collapse"
                  href="#ywbox-nav-browse-file"
                  role="button"
                  aria-expanded="false"
                  aria-controls="ywbox-nav-browse-file"
                >
                  {t('browseFiles')}
                </a>
              </li>
            </ul>

            <div
              ref={uploadFilePanelRef}
              className="collapse show"
              id="ywbox-nav-upload-file"
            >
              <div className="my-4">
                <FileAlert type={alertType} message={alert} />

                <div className="input-group">
                  <input
                    type="file"
                    accept=".zip,.rar,.7z,image/*"
                    id="ywbox-input-uploadFile"
                    className="form-control"
                    name="uploadFile"
                    value={form.uploadFile}
                    onChange={onChangeForm}
                    aria-describedby={t('uploadCover')}
                    aria-label="Upload"
                  />
                  <button
                    disabled={
                      uploadPostContentMutation.isLoading ||
                      uploadSectionContentMutation.isLoading ||
                      uploadPostNewContentMutation.isLoading
                    }
                    onClick={onClickUploadFileBtn}
                    className="btn btn-outline-secondary"
                    type="button"
                  >
                    {(uploadPostContentMutation.isLoading ||
                      uploadSectionContentMutation.isLoading ||
                      uploadPostNewContentMutation.isLoading) && (
                      <div
                        className="spinner-border text-secondary me-2 spinner-border-sm"
                        role="status"
                      >
                        <span className="visually-hidden">Loading...</span>
                      </div>
                    )}
                    {t('startUpload')}
                  </button>
                </div>
              </div>
            </div>
            <div
              ref={browseFilePanelRef}
              className="collapse"
              id="ywbox-nav-browse-file"
            >
              <div className="my-4">
                {clientQueryUserFilesQuery.isError ? (
                  <FileAlert
                    type="DANGER"
                    message={clientQueryUserFilesQuery.error}
                  />
                ) : (
                  <FileAlert
                    type={browseFileAlertType}
                    message={browseFileAlert}
                  />
                )}

                <div className="row gap-2">
                  <div className="col">
                    <div
                      className="btn-group"
                      role="group"
                      aria-label="Basic outlined example"
                    >
                      <button
                        onClick={onClickDeleteFileBtn}
                        type="button"
                        className="btn btn-outline-primary"
                      >
                        <i className="bi bi-trash me-2"></i>
                        {t('delete')}
                      </button>
                    </div>
                  </div>
                </div>

                <hr />

                {clientQueryUserFilesQuery.isError ? (
                  <FileListPlaceholder isError={true} />
                ) : (
                  <>
                    {clientQueryUserFilesQuery.isLoading ||
                    clientQueryUserFilesQuery.isFetching ? (
                      <FileListPlaceholder />
                    ) : (
                      <div className="row row-cols-auto">
                        {(clientQueryUserFilesQuery.data as IClientFile[]).map(
                          (item) => {
                            return (
                              <div key={item.id} className="col mb-3">
                                <FileItem
                                  item={item}
                                  onClickBrowseFileItem={onClickBrowseFileItem.bind(
                                    undefined,
                                    item,
                                  )}
                                  browseFileItemIndex={browseFileItemIndex}
                                  oss={oss}
                                />
                              </div>
                            );
                          },
                        )}
                      </div>
                    )}
                  </>
                )}
              </div>
            </div>
          </div>
          <div className="modal-footer">
            <button
              onClick={onClickCloseModalBtn}
              type="button"
              className="btn btn-secondary"
              aria-label="Close"
            >
              {t('close')}
            </button>
            <button
              onClick={onClickSelectFileBtn}
              type="button"
              className="btn btn-primary"
            >
              {t('select')}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
});

const FileItem = ({
  item,
  onClickBrowseFileItem,
  browseFileItemIndex,
  oss,
}: {
  item: IClientFile;
  onClickBrowseFileItem: MouseEventHandler;
  browseFileItemIndex: number;
  oss?: string;
}) => {
  const url = oss ? oss + item.urls.default : item.urls.default;
  const [load, setLoad] = useState(false);

  function onLoad() {
    setLoad(true);
  }

  return (
    <div
      onClick={onClickBrowseFileItem}
      className={classNames(
        'card cursor-pointer position-relative user-select-none',
        {
          'border-success': browseFileItemIndex === item.id,
        },
      )}
      style={{ width: '12rem', height: '15rem' }}
    >
      <div className="ratio ratio-4x3">
        {item.isImage ? (
          <>
            {!load && (
              <div className="d-flex justify-content-center align-items-center text-muted">
                <p className="placeholder-glow w-25 mb-0">
                  <span className="placeholder col-12"></span>
                </p>
              </div>
            )}
            <Image
              src={url}
              alt={item.originalName}
              onLoad={onLoad}
              fill
              sizes="100vw"
              style={{
                objectFit: 'cover',
              }}
            />
          </>
        ) : (
          <div className="d-flex justify-content-center align-items-center text-muted text-uppercase">
            {item.extension || 'FILE'}
          </div>
        )}
      </div>
      <div className="card-body">
        {item.isImage && !load ? (
          <>
            <p className="card-title placeholder-glow">
              <span className="placeholder col-8"></span>
            </p>
            <p className="card-text placeholder-glow">
              <span className="placeholder col-6"></span>
            </p>
          </>
        ) : (
          <>
            <p className="card-title text-truncate">{item.originalName}</p>
            <p className="card-text text-muted">
              {Math.ceil(item.size / 1024)}KB
            </p>
          </>
        )}
      </div>
      {browseFileItemIndex === item.id && (
        <div className="position-absolute top-50 start-50 translate-middle">
          <div className="animate__animated animate__pulse">
            <i className="bi bi-check2-circle text-success fs-4"></i>
          </div>
        </div>
      )}
    </div>
  );
};

const FileAlert = ({
  type = 'LIGHT',
  message,
}: {
  type: TMessageType;
  message: TError | any;
}) => {
  return (
    <div
      className={classNames('alert', {
        'alert-light': type === 'LIGHT',
        'alert-danger': type === 'DANGER',
        'alert-success': type === 'SUCCESS',
        'alert-info': type === 'INFO',
      })}
      role="alert"
    >
      {message}
    </div>
  );
};

const FileListPlaceholder = ({ isError = false }) => {
  const t = useTranslations('PostEditPage');

  return (
    <div className="my-5 ms-4">
      {isError ? (
        t('loadFileListFailed')
      ) : (
        <div className="spinner-border text-secondary" role="status">
          <span className="visually-hidden">Loading...</span>
        </div>
      )}
    </div>
  );
};
